const { v4: uuidv4 } = require('uuid');
const downloadsRepo = require('../db/repositories/downloadsRepo');
const logsRepo = require('../db/repositories/logsRepo');
const { logger: baseLogger, childWithReqId } = require('../utils/logger');
const { validateDownloadInput } = require('../utils/validator');
const { downloadToFile } = require('./fileService');

const STATUS = {
    PENDING: 'PENDING',
    RUNNING: 'RUNNING',
    COMPLETED: 'COMPLETED',
    FAILED: 'FAILED',
    CANCELED: 'CANCELED',
};

function createJob(payload, reqId) {
    const log = childWithReqId(reqId) || baseLogger;

    const validation = validateDownloadInput(payload);
    if (!validation.ok) {
        const message = `invalid payload: ${validation.errors.join(',')}`;
        log.warn({ errors: validation.errors }, 'job creation rejected');
        const error = new Error(message);
        error.code = 'VALIDATION_ERROR';
        throw error;
    }

    const id = uuidv4();
    const job = {
        id,
        file_url: payload.fileUrl,
        file_name: payload.fileName,
        save_path: payload.savePath || '.',
        status: STATUS.PENDING,
        total_bytes: 0,
        received_bytes: 0,
        percent: 0,
        speed_bps: 0,
        error_code: null,
        error_message: null,
    };

    downloadsRepo.createDownload(job);
    logsRepo.appendLog(id, 'info', 'job created');
    log.info({ jobId: id }, 'job created');
    return { id };
}

function getJob(jobId) {
    return downloadsRepo.getById(jobId);
}

function listJobs({ status = null, limit = 50, offset = 0 } = {}) {
    if (status) {
        return downloadsRepo.listByStatus(status, limit, offset);
    }
    return downloadsRepo.listAll(limit, offset);
}

async function runJob(jobId, reqId) {
    const log = childWithReqId(reqId) || baseLogger;
    const job = downloadsRepo.getById(jobId);
    if (!job) {
        const error = new Error('job not found');
        error.code = 'NOT_FOUND';
        throw error;
    }
    if (job.status !== STATUS.PENDING && job.status !== STATUS.FAILED) {
        return job;
    }

    downloadsRepo.updateDownload(jobId, { status: STATUS.RUNNING, error_code: null, error_message: null });
    logsRepo.appendLog(jobId, 'info', 'job started');
    log.info({ jobId }, 'job started');

    try {
        await downloadToFile(
            job,
            log,
            ({ receivedBytes, totalBytes, percent, speedBps }) => {
                downloadsRepo.updateDownload(jobId, {
                    received_bytes: receivedBytes,
                    total_bytes: totalBytes ?? job.total_bytes,
                    percent: percent ?? job.percent,
                    speed_bps: speedBps,
                });
            }
        );

        downloadsRepo.updateDownload(jobId, { status: STATUS.COMPLETED, speed_bps: 0, percent: 100 });
        logsRepo.appendLog(jobId, 'info', 'job completed');
        log.info({ jobId }, 'job completed');
    } catch (err) {
        const message = err && err.message ? err.message : 'download failed';
        const code = err && err.code ? err.code : 'UNKNOWN_ERROR';
        downloadsRepo.updateDownload(jobId, { status: STATUS.FAILED, error_code: code, error_message: message });
        logsRepo.appendLog(jobId, 'error', message, { code });
        log.error({ jobId, err }, 'job failed');
        throw err;
    }

    return downloadsRepo.getById(jobId);
}

module.exports = {
    STATUS,
    createJob,
    getJob,
    listJobs,
    runJob,
};


